#ifndef MYHANDLER_H
#define MYHANDLER_H

#include <BLineArray>
#include <BCone>
#include <BSphere>
#include <BPickHit>
#include <BRoundHandler>

#include "TransformHandler.h"
#include "TransOpera.h"
#include "Target.h"

class Frustum
{
public:
	inline bool clip(const BVector& v) const
	{
		if (plane_near.clip(v))
			return true;
		if (plane_far.clip(v))
			return true;
		if (plane_left.clip(v))
			return true;
		if (plane_right.clip(v))
			return true;
		if (plane_top.clip(v))
			return true;
		if (plane_bottom.clip(v))
			return true;
		return false;
	}
	inline bool clip(const BBox& box) const
	{
		if (plane_near.clip(box))
			return true;
		if (plane_far.clip(box))
			return true;
		if (plane_left.clip(box))
			return true;
		if (plane_right.clip(box))
			return true;
		if (plane_top.clip(box))
			return true;
		if (plane_bottom.clip(box))
			return true;
		return false;
	}
	inline bool clip(const BSpace& space) const
	{
		if (plane_near.clip(space))
			return true;
		if (plane_far.clip(space))
			return true;
		if (plane_left.clip(space))
			return true;
		if (plane_right.clip(space))
			return true;
		if (plane_top.clip(space))
			return true;
		if (plane_bottom.clip(space))
			return true;
		return false;
	}

	inline BSpace space() const
	{
		BSpace s(near_v0);
		s.expand(near_v1);
		s.expand(near_v2);
		s.expand(near_v3);
		s.expand(far_v0);
		s.expand(far_v1);
		s.expand(far_v2);
		s.expand(far_v3);
		return s;
	}

	inline void operator *= (const BMatrix& matrix)
	{
		near_v0 *= matrix;
		near_v1 *= matrix;
		near_v2 *= matrix;
		near_v3 *= matrix;
		far_v0 *= matrix;
		far_v1 *= matrix;
		far_v2 *= matrix;
		far_v3 *= matrix;
		freshPlanes();
	}
	inline Frustum operator * (const BMatrix& matrix)
	{
		Frustum temp = *this;
		temp.near_v0 *= matrix;
		temp.near_v1 *= matrix;
		temp.near_v2 *= matrix;
		temp.near_v3 *= matrix;
		temp.far_v0 *= matrix;
		temp.far_v1 *= matrix;
		temp.far_v2 *= matrix;
		temp.far_v3 *= matrix;
		temp.freshPlanes();
		return temp;
	}

	inline void freshPlanes()
	{
		plane_near.set(near_v2, near_v1, near_v0);
		plane_far.set(far_v0, far_v1, far_v2);
		plane_left.set(far_v0, near_v0, near_v1);
		plane_bottom.set(far_v1, near_v1, near_v2);
		plane_right.set(far_v2, near_v2, near_v3);
		plane_top.set(far_v3, near_v3, near_v0);
	}

	BVector				near_v0;
	BVector				near_v1;
	BVector				near_v2;
	BVector				near_v3;
	BVector				far_v0;
	BVector				far_v1;
	BVector				far_v2;
	BVector				far_v3;
	BPlane				plane_near;
	BPlane				plane_far;
	BPlane				plane_left;
	BPlane				plane_right;
	BPlane				plane_top;
	BPlane				plane_bottom;
};

class MyHandler : public BRoundHandler
{
public:
	MyHandler();
	~MyHandler();

	void setGridLineVisible(bool gridLineVisible);
	bool gridLineVisible() const;

	void setGridSize(const BRealSize& gridSize);
	const BRealSize& gridSize() const;

	void setGridSegment(int gridSegment);
	int gridSegment() const;

	void setGridColor(const BColor& color);
	const BColor& gridColor() const;

	void setShowNormals(bool showNormals);
	bool showNormals() const;

	void setNormalsFlags(const BString& normalsFlag);
	const BString& normalsFlag() const;

	void setNormalLengthRatio(float normalLengthRatio);
	float normalLengthRatio() const;

	void setHandleMode(HandleMode hmode);
	HandleMode handleMode() const;

	void setHandleAxis(HandleAxis haxis);
	HandleAxis handleAxis() const;

	void setVisibleBaseAxes(int visibleAxes);
	int visibleAxes() const;

	void setBaseAxisXColor(const BColor& baseAxisXColor);
	const BColor& baseAxisXColor() const;

	void setBaseAxisYColor(const BColor& baseAxisYColor);
	const BColor& baseAxisYColor() const;

	void setBaseAxisZColor(const BColor& baseAxisZColor);
	const BColor& baseAxisZColor() const;

	using BRoundHandler::focus;
	virtual void focus(const BSpace& space);
	virtual void focus(const BBox& box);

protected:
	BLineArray* createFaceNormalLines(BGeometry* geometry);
	BLineArray* createFaceNormalLines(BMesh* mesh);
	void drawNormals(BShape* shape, BRender& render);
	void freshFrustum();
	void frustumPick();

	bool comparePickItem(const BPickHit* hitA, const BPickHit* hitB);

	void startTransform(const BMatrix& matrix);
	void transforming(const BMatrix& matrix);
	void finishTransform(const BMatrix& matrix);

protected:
	void freshTargetMatrix();
	void freshAxis();
	void freshMoveAxis();
	void freshRotateAxis();
	void freshScaleAxis();

	bool pickAxis(const BLine& line);
	bool pickMoveAxis(const BLine& line);
	bool pickRotateAxis(const BLine& line);
	bool pickScaleAxis(const BLine& line);

	void applyOpera(const BLine& line);
	void applyMoveOpera(const BLine& line);
	void applyRotateOpera(const BLine& line);
	void applyScaleOpera(const BLine& line);

	void drawAxis(BRender& render);
	void drawAnchorAxis(BRender& render);
	void drawMoveAxis(BRender& render);
	void drawRotateAxis(BRender& render);
	void drawScaleAxis(BRender& render);

protected:
	void slotTargetChanged(BObject* object, const BValue& value);

protected:
	virtual void attached(BViewer* viewer) override;
	virtual void detached(BViewer* viewer) override;

	virtual void paint(BPainter& painter);
	virtual void render(BRender& render);

	virtual void updateEvent(const BEvent& event);
	virtual void styleEvent(const BEvent& event);
	virtual void freshEvent(const BEvent& event);

	virtual void keyPressEvent(const BKeyEvent& keyEvent);
	virtual void keyReleaseEvent(const BKeyEvent& keyEvent);

	virtual void mousePressEvent(const BMouseEvent& mouseEvent);
	virtual void mouseReleaseEvent(const BMouseEvent& mouseEvent);
	virtual void mouseMoveEvent(const BMouseEvent& mouseEvent);
	virtual void mouseClickEvent(const BMouseEvent& mouseEvent);

private:
	bool					_gridLinesVisible;
	bool					_dirtyGridLines;
	BRealSize				_gridSize;
	BColor					_gridColor;
	int						_gridSegment;
	BLineArray				_horLines;
	BLineArray				_verLines;

	int						_visibleBaseAxes;
	BColor					_baseAxisXColor;
	BColor					_baseAxisYColor;
	BColor					_baseAxisZColor;
	BLine					_baseAxisXLine;
	BLine					_baseAxisYLine;
	BLine					_baseAxisZLine;

	BReal					_axisSize;
	HandleMode				_handleMode;
	HandleAxis				_handleAxis;

	bool					_dirtyAxis;
	BLine					_axis_x;
	BLine					_axis_y;
	BLine					_axis_z;
	BConeHolder				_cone_x;
	BConeHolder				_cone_y;
	BConeHolder				_cone_z;
	BQuadf					_quad_xy;
	BQuadf					_quad_yz;
	BQuadf					_quad_xz;

	BVectorArray			_rotateCircleXY;
	BVectorArray			_rotateCircleYZ;
	BVectorArray			_rotateCircleXZ;
	BHolder<BSphere>		_rotateSphere;

	BHolder<Target>			_target;
	BMatrix					_basicMatrix;
	BMatrix					_origin;
	BMatrix					_matrix;
	Frustum					_frustum;
	bool					_showNormals;
	float					_normalLengthRatio;
	BString					_normalsFlag;

	BHolder<BPickHit>		_hoveredHit;
	BVector					_focusPoint;
	bool					_ctrlDown;
	bool					_leftPressed;
	BPoint					_pressedPoint;
	BRect					_selectRect;
	BLine					_linePressed;
	BHolder<TransOpera>		_transOpera;

	//for smooth slide camera to target.
	int						_slideTimes;
	BVector					_slidePosition;
	BVector					_slideCenter;
	BVector					_slideUp;

};

#endif